{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Interactive Data Preparation\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before training the model, you should clean the data and create meaningful features that will be good predictors for the target variable (was there a fraud?). The `interactive-data-prep.ipynb` notebook demonstrates how to interactively build features for training the model. While this approach is simple, it is unsuitable for production environments with continuous data ingestion, large scale, or real-time. In the next section, you will implement the same logic for production using a feature store."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The training set is built from three datasets: credit transactions, user events, and labels indicating if there was fraud. In this example, we prepare each dataset separately and combine them later for training."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preparing the Credit Transaction Dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following transformations create more meaningful features, which can have a more significant impact on the prediction than the raw data:\n",
    "    \n",
    "- Extracting the date components (hour, day of week) from the timestamp.\n",
    "- One-hot encoding for the age groups, transaction category, and the gender.\n",
    "- Aggregating the amount (avg., sum, count, max over 2/12/24 hour time win‐ dows).\n",
    "- Aggregating the transactions per category (over 14 days time windows).\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Building categorical features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>step</th>\n",
       "      <th>age</th>\n",
       "      <th>gender</th>\n",
       "      <th>zipcodeOri</th>\n",
       "      <th>zipMerchant</th>\n",
       "      <th>category</th>\n",
       "      <th>amount</th>\n",
       "      <th>fraud</th>\n",
       "      <th>timestamp</th>\n",
       "      <th>source</th>\n",
       "      <th>target</th>\n",
       "      <th>device</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>274633</th>\n",
       "      <td>91</td>\n",
       "      <td>5</td>\n",
       "      <td>F</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>es_transportation</td>\n",
       "      <td>26.92</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:00:42.615892000</td>\n",
       "      <td>C1022153336</td>\n",
       "      <td>M1823072687</td>\n",
       "      <td>33832bb8607545df97632a7ab02d69c4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>286902</th>\n",
       "      <td>94</td>\n",
       "      <td>2</td>\n",
       "      <td>M</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>es_transportation</td>\n",
       "      <td>48.22</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:00.909517913</td>\n",
       "      <td>C1006176917</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>fadd829c49e74ffa86c8da3be75ada53</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>416998</th>\n",
       "      <td>131</td>\n",
       "      <td>3</td>\n",
       "      <td>M</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>es_transportation</td>\n",
       "      <td>17.56</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:06.016687939</td>\n",
       "      <td>C1010936270</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>58d0422a50bc40c89d2b4977b2f1beea</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        step age gender  zipcodeOri  zipMerchant           category  amount  \\\n",
       "274633    91   5      F       28007        28007  es_transportation   26.92   \n",
       "286902    94   2      M       28007        28007  es_transportation   48.22   \n",
       "416998   131   3      M       28007        28007  es_transportation   17.56   \n",
       "\n",
       "        fraud                     timestamp       source       target  \\\n",
       "274633      0 2023-08-07 22:00:42.615892000  C1022153336  M1823072687   \n",
       "286902      0 2023-08-07 22:01:00.909517913  C1006176917   M348934600   \n",
       "416998      0 2023-08-07 22:01:06.016687939  C1010936270   M348934600   \n",
       "\n",
       "                                  device  \n",
       "274633  33832bb8607545df97632a7ab02d69c4  \n",
       "286902  fadd829c49e74ffa86c8da3be75ada53  \n",
       "416998  58d0422a50bc40c89d2b4977b2f1beea  "
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "from src.date_adjust import adjust_data_timespan\n",
    "import mlrun\n",
    "\n",
    "# Fetch the transactions and event datasets from mlrun data samples \n",
    "data_path = mlrun.get_sample_path(\"data/fraud-demo-mlrun-fs-docs/\")\n",
    "transactions_data = pd.read_csv(data_path + \"data.csv\", parse_dates=['timestamp'])\n",
    "\n",
    "# use only first 10k\n",
    "transactions_data = transactions_data.sort_values(by='source', axis=0)[:10000]\n",
    "\n",
    "# Adjust the samples timestamp for the past 2 days\n",
    "transactions_data = adjust_data_timespan(transactions_data, new_period='2d')\n",
    "\n",
    "# Preview\n",
    "transactions_data.head(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Index(['step', 'age', 'gender', 'zipcodeOri', 'zipMerchant', 'category',\n",
       "       'amount', 'fraud', 'timestamp', 'source', 'target', 'device'],\n",
       "      dtype='object')"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "transactions_data.columns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next part is aggregating the transaction amounts by time windows and transaction categories, providing you with a long list of derived features that can potentially help make better predictions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>step</th>\n",
       "      <th>age</th>\n",
       "      <th>zipcodeOri</th>\n",
       "      <th>zipMerchant</th>\n",
       "      <th>amount</th>\n",
       "      <th>fraud</th>\n",
       "      <th>timestamp</th>\n",
       "      <th>source</th>\n",
       "      <th>target</th>\n",
       "      <th>device</th>\n",
       "      <th>...</th>\n",
       "      <th>category_es_hyper</th>\n",
       "      <th>category_es_leisure</th>\n",
       "      <th>category_es_otherservices</th>\n",
       "      <th>category_es_sportsandtoys</th>\n",
       "      <th>category_es_tech</th>\n",
       "      <th>category_es_transportation</th>\n",
       "      <th>category_es_travel</th>\n",
       "      <th>category_es_wellnessandbeauty</th>\n",
       "      <th>gender_F</th>\n",
       "      <th>gender_M</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>274633</th>\n",
       "      <td>91</td>\n",
       "      <td>5</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>26.92</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:00:42.615892000</td>\n",
       "      <td>C1022153336</td>\n",
       "      <td>M1823072687</td>\n",
       "      <td>33832bb8607545df97632a7ab02d69c4</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>286902</th>\n",
       "      <td>94</td>\n",
       "      <td>2</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>48.22</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:00.909517913</td>\n",
       "      <td>C1006176917</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>fadd829c49e74ffa86c8da3be75ada53</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>416998</th>\n",
       "      <td>131</td>\n",
       "      <td>3</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>17.56</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:06.016687939</td>\n",
       "      <td>C1010936270</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>58d0422a50bc40c89d2b4977b2f1beea</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>334543</th>\n",
       "      <td>108</td>\n",
       "      <td>4</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>4.50</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:18.309439118</td>\n",
       "      <td>C1033736586</td>\n",
       "      <td>M1823072687</td>\n",
       "      <td>30b269ae55984e5584f1dd5f642ac1a3</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>210647</th>\n",
       "      <td>72</td>\n",
       "      <td>4</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>1.83</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:52.198521001</td>\n",
       "      <td>C1019071188</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>97bee3503a984f59aa6139b59f933c0b</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 30 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "        step age  zipcodeOri  zipMerchant  amount  fraud  \\\n",
       "274633    91   5       28007        28007   26.92      0   \n",
       "286902    94   2       28007        28007   48.22      0   \n",
       "416998   131   3       28007        28007   17.56      0   \n",
       "334543   108   4       28007        28007    4.50      0   \n",
       "210647    72   4       28007        28007    1.83      0   \n",
       "\n",
       "                           timestamp       source       target  \\\n",
       "274633 2023-08-07 22:00:42.615892000  C1022153336  M1823072687   \n",
       "286902 2023-08-07 22:01:00.909517913  C1006176917   M348934600   \n",
       "416998 2023-08-07 22:01:06.016687939  C1010936270   M348934600   \n",
       "334543 2023-08-07 22:01:18.309439118  C1033736586  M1823072687   \n",
       "210647 2023-08-07 22:01:52.198521001  C1019071188   M348934600   \n",
       "\n",
       "                                  device  ...  category_es_hyper  \\\n",
       "274633  33832bb8607545df97632a7ab02d69c4  ...                  0   \n",
       "286902  fadd829c49e74ffa86c8da3be75ada53  ...                  0   \n",
       "416998  58d0422a50bc40c89d2b4977b2f1beea  ...                  0   \n",
       "334543  30b269ae55984e5584f1dd5f642ac1a3  ...                  0   \n",
       "210647  97bee3503a984f59aa6139b59f933c0b  ...                  0   \n",
       "\n",
       "        category_es_leisure category_es_otherservices  \\\n",
       "274633                    0                         0   \n",
       "286902                    0                         0   \n",
       "416998                    0                         0   \n",
       "334543                    0                         0   \n",
       "210647                    0                         0   \n",
       "\n",
       "        category_es_sportsandtoys  category_es_tech  \\\n",
       "274633                          0                 0   \n",
       "286902                          0                 0   \n",
       "416998                          0                 0   \n",
       "334543                          0                 0   \n",
       "210647                          0                 0   \n",
       "\n",
       "        category_es_transportation  category_es_travel  \\\n",
       "274633                           1                   0   \n",
       "286902                           1                   0   \n",
       "416998                           1                   0   \n",
       "334543                           1                   0   \n",
       "210647                           1                   0   \n",
       "\n",
       "        category_es_wellnessandbeauty  gender_F  gender_M  \n",
       "274633                              0         1         0  \n",
       "286902                              0         0         1  \n",
       "416998                              0         0         1  \n",
       "334543                              0         1         0  \n",
       "210647                              0         0         1  \n",
       "\n",
       "[5 rows x 30 columns]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "processed_transactions = transactions_data\n",
    "\n",
    "# Generate day and hour columns from the timestamp\n",
    "processed_transactions['day_of_week'] = processed_transactions['timestamp'].dt.weekday\n",
    "processed_transactions['hour'] = processed_transactions['timestamp'].dt.hour\n",
    "\n",
    "# Map age groups\n",
    "processed_transactions[\"age_mapped\"] = processed_transactions[\"age\"].map(\n",
    "    lambda x: {'U': '0'}.get(x, x)\n",
    ")\n",
    "\n",
    "# encode categories and gender groups (using one hot encoding)\n",
    "processed_transactions = pd.get_dummies(processed_transactions, columns=['category', 'gender'])\n",
    "processed_transactions.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "transactions_for_agg = processed_transactions.set_index(['timestamp'],)\n",
    "\n",
    "# Group/Aggregate amount stats (mean, max, ..) by time windows\n",
    "windows=['2H', '12H', '24H']\n",
    "operation = ['mean','sum', 'count','max']\n",
    "for window in windows:\n",
    "    for op in operation:\n",
    "        processed_transactions[f'amount_{op}_{window}'] = transactions_for_agg.groupby(['source', pd.Grouper(freq=window)])['amount'].transform(op).values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>step</th>\n",
       "      <th>age</th>\n",
       "      <th>zipcodeOri</th>\n",
       "      <th>zipMerchant</th>\n",
       "      <th>amount</th>\n",
       "      <th>fraud</th>\n",
       "      <th>timestamp</th>\n",
       "      <th>target</th>\n",
       "      <th>device</th>\n",
       "      <th>day_of_week</th>\n",
       "      <th>...</th>\n",
       "      <th>es_barsandrestaurants_sum_14D</th>\n",
       "      <th>es_tech_sum_14D</th>\n",
       "      <th>es_sportsandtoys_sum_14D</th>\n",
       "      <th>es_wellnessandbeauty_sum_14D</th>\n",
       "      <th>es_hyper_sum_14D</th>\n",
       "      <th>es_fashion_sum_14D</th>\n",
       "      <th>es_home_sum_14D</th>\n",
       "      <th>es_contents_sum_14D</th>\n",
       "      <th>es_travel_sum_14D</th>\n",
       "      <th>es_leisure_sum_14D</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>source</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>C1022153336</th>\n",
       "      <td>91</td>\n",
       "      <td>5</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>26.92</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:00:42.615892000</td>\n",
       "      <td>M1823072687</td>\n",
       "      <td>33832bb8607545df97632a7ab02d69c4</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1006176917</th>\n",
       "      <td>94</td>\n",
       "      <td>2</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>48.22</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:00.909517913</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>fadd829c49e74ffa86c8da3be75ada53</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1010936270</th>\n",
       "      <td>131</td>\n",
       "      <td>3</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>17.56</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:06.016687939</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>58d0422a50bc40c89d2b4977b2f1beea</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>6</td>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1033736586</th>\n",
       "      <td>108</td>\n",
       "      <td>4</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>4.50</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:18.309439118</td>\n",
       "      <td>M1823072687</td>\n",
       "      <td>30b269ae55984e5584f1dd5f642ac1a3</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>3</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1019071188</th>\n",
       "      <td>72</td>\n",
       "      <td>4</td>\n",
       "      <td>28007</td>\n",
       "      <td>28007</td>\n",
       "      <td>1.83</td>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:52.198521001</td>\n",
       "      <td>M348934600</td>\n",
       "      <td>97bee3503a984f59aa6139b59f933c0b</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 56 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "             step age  zipcodeOri  zipMerchant  amount  fraud  \\\n",
       "source                                                          \n",
       "C1022153336    91   5       28007        28007   26.92      0   \n",
       "C1006176917    94   2       28007        28007   48.22      0   \n",
       "C1010936270   131   3       28007        28007   17.56      0   \n",
       "C1033736586   108   4       28007        28007    4.50      0   \n",
       "C1019071188    72   4       28007        28007    1.83      0   \n",
       "\n",
       "                                timestamp       target  \\\n",
       "source                                                   \n",
       "C1022153336 2023-08-07 22:00:42.615892000  M1823072687   \n",
       "C1006176917 2023-08-07 22:01:00.909517913   M348934600   \n",
       "C1010936270 2023-08-07 22:01:06.016687939   M348934600   \n",
       "C1033736586 2023-08-07 22:01:18.309439118  M1823072687   \n",
       "C1019071188 2023-08-07 22:01:52.198521001   M348934600   \n",
       "\n",
       "                                       device  day_of_week  ...  \\\n",
       "source                                                      ...   \n",
       "C1022153336  33832bb8607545df97632a7ab02d69c4            0  ...   \n",
       "C1006176917  fadd829c49e74ffa86c8da3be75ada53            0  ...   \n",
       "C1010936270  58d0422a50bc40c89d2b4977b2f1beea            0  ...   \n",
       "C1033736586  30b269ae55984e5584f1dd5f642ac1a3            0  ...   \n",
       "C1019071188  97bee3503a984f59aa6139b59f933c0b            0  ...   \n",
       "\n",
       "             es_barsandrestaurants_sum_14D es_tech_sum_14D  \\\n",
       "source                                                       \n",
       "C1022153336                              1               1   \n",
       "C1006176917                              4               0   \n",
       "C1010936270                              4               0   \n",
       "C1033736586                              3               2   \n",
       "C1019071188                              1               0   \n",
       "\n",
       "             es_sportsandtoys_sum_14D  es_wellnessandbeauty_sum_14D  \\\n",
       "source                                                                \n",
       "C1022153336                         1                             1   \n",
       "C1006176917                         1                             1   \n",
       "C1010936270                         0                             6   \n",
       "C1033736586                         0                             1   \n",
       "C1019071188                         0                             0   \n",
       "\n",
       "             es_hyper_sum_14D  es_fashion_sum_14D  es_home_sum_14D  \\\n",
       "source                                                               \n",
       "C1022153336                 0                   1                0   \n",
       "C1006176917                 0                   2                0   \n",
       "C1010936270                 6                   0                0   \n",
       "C1033736586                 3                   0                2   \n",
       "C1019071188                 1                   4                0   \n",
       "\n",
       "             es_contents_sum_14D  es_travel_sum_14D  es_leisure_sum_14D  \n",
       "source                                                                   \n",
       "C1022153336                    0                  0                   0  \n",
       "C1006176917                    0                  0                   0  \n",
       "C1010936270                    0                  0                   0  \n",
       "C1033736586                    0                  1                   0  \n",
       "C1019071188                    1                  1                   0  \n",
       "\n",
       "[5 rows x 56 columns]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Group/Aggregate amount stats (mean, max, ..) by transaction category\n",
    "main_categories = [\"es_transportation\", \"es_health\", \"es_otherservices\",\n",
    "       \"es_food\", \"es_hotelservices\", \"es_barsandrestaurants\",\n",
    "       \"es_tech\", \"es_sportsandtoys\", \"es_wellnessandbeauty\",\n",
    "       \"es_hyper\", \"es_fashion\", \"es_home\", \"es_contents\",\n",
    "       \"es_travel\", \"es_leisure\"]\n",
    "for category in main_categories:\n",
    "    processed_transactions[f'{category}_sum_14D'] = transactions_for_agg.groupby(['source', pd.Grouper(freq='14D')])[f'category_{category}'].transform('sum').values\n",
    "\n",
    "processed_transactions.set_index(['source'], inplace=True)\n",
    "processed_transactions.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "step                                       int64\n",
       "age                                       object\n",
       "zipcodeOri                                 int64\n",
       "zipMerchant                                int64\n",
       "amount                                   float64\n",
       "fraud                                      int64\n",
       "timestamp                         datetime64[ns]\n",
       "target                                    object\n",
       "device                                    object\n",
       "day_of_week                                int64\n",
       "hour                                       int64\n",
       "age_mapped                                object\n",
       "category_es_barsandrestaurants             uint8\n",
       "category_es_contents                       uint8\n",
       "category_es_fashion                        uint8\n",
       "category_es_food                           uint8\n",
       "category_es_health                         uint8\n",
       "category_es_home                           uint8\n",
       "category_es_hotelservices                  uint8\n",
       "category_es_hyper                          uint8\n",
       "category_es_leisure                        uint8\n",
       "category_es_otherservices                  uint8\n",
       "category_es_sportsandtoys                  uint8\n",
       "category_es_tech                           uint8\n",
       "category_es_transportation                 uint8\n",
       "category_es_travel                         uint8\n",
       "category_es_wellnessandbeauty              uint8\n",
       "gender_F                                   uint8\n",
       "gender_M                                   uint8\n",
       "amount_mean_2H                           float64\n",
       "amount_sum_2H                            float64\n",
       "amount_count_2H                            int64\n",
       "amount_max_2H                            float64\n",
       "amount_mean_12H                          float64\n",
       "amount_sum_12H                           float64\n",
       "amount_count_12H                           int64\n",
       "amount_max_12H                           float64\n",
       "amount_mean_24H                          float64\n",
       "amount_sum_24H                           float64\n",
       "amount_count_24H                           int64\n",
       "amount_max_24H                           float64\n",
       "es_transportation_sum_14D                  uint8\n",
       "es_health_sum_14D                          uint8\n",
       "es_otherservices_sum_14D                   uint8\n",
       "es_food_sum_14D                            uint8\n",
       "es_hotelservices_sum_14D                   uint8\n",
       "es_barsandrestaurants_sum_14D              uint8\n",
       "es_tech_sum_14D                            uint8\n",
       "es_sportsandtoys_sum_14D                   uint8\n",
       "es_wellnessandbeauty_sum_14D               uint8\n",
       "es_hyper_sum_14D                           uint8\n",
       "es_fashion_sum_14D                         uint8\n",
       "es_home_sum_14D                            uint8\n",
       "es_contents_sum_14D                        uint8\n",
       "es_travel_sum_14D                          uint8\n",
       "es_leisure_sum_14D                         uint8\n",
       "dtype: object"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "processed_transactions.dtypes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preparing the User Events(Activities) Dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The events dataset contains user activities such as login, change of details, or password, which can hint at a fraud attempt. The next part shows how to load the events dataset and create categorical features per event type."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Processing the events dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>source</th>\n",
       "      <th>event</th>\n",
       "      <th>timestamp</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>45553</th>\n",
       "      <td>C137986193</td>\n",
       "      <td>password_change</td>\n",
       "      <td>2023-08-07 22:00:43.370480000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24134</th>\n",
       "      <td>C1940951230</td>\n",
       "      <td>details_change</td>\n",
       "      <td>2023-08-07 22:00:44.418662091</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>64444</th>\n",
       "      <td>C247537602</td>\n",
       "      <td>login</td>\n",
       "      <td>2023-08-07 22:00:46.073445103</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            source            event                     timestamp\n",
       "45553   C137986193  password_change 2023-08-07 22:00:43.370480000\n",
       "24134  C1940951230   details_change 2023-08-07 22:00:44.418662091\n",
       "64444   C247537602            login 2023-08-07 22:00:46.073445103"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Fetch the user_events dataset from the server\n",
    "user_events_data = pd.read_csv(data_path + \"events.csv\", \n",
    "                               index_col=0, quotechar=\"\\'\", parse_dates=['timestamp'])\n",
    "\n",
    "# Adjust to the last 2 days to see the latest aggregations in the online feature vectors\n",
    "user_events_data = adjust_data_timespan(user_events_data, new_period='2d')\n",
    "\n",
    "# Preview\n",
    "user_events_data.head(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>timestamp</th>\n",
       "      <th>event_details_change</th>\n",
       "      <th>event_login</th>\n",
       "      <th>event_password_change</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>source</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>C137986193</th>\n",
       "      <td>2023-08-07 22:00:43.370480000</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1940951230</th>\n",
       "      <td>2023-08-07 22:00:44.418662091</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C247537602</th>\n",
       "      <td>2023-08-07 22:00:46.073445103</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C470079617</th>\n",
       "      <td>2023-08-07 22:00:47.363894428</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1142118359</th>\n",
       "      <td>2023-08-07 22:00:48.154186830</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                timestamp  event_details_change  event_login  \\\n",
       "source                                                                         \n",
       "C137986193  2023-08-07 22:00:43.370480000                     0            0   \n",
       "C1940951230 2023-08-07 22:00:44.418662091                     1            0   \n",
       "C247537602  2023-08-07 22:00:46.073445103                     0            1   \n",
       "C470079617  2023-08-07 22:00:47.363894428                     0            0   \n",
       "C1142118359 2023-08-07 22:00:48.154186830                     0            1   \n",
       "\n",
       "             event_password_change  \n",
       "source                              \n",
       "C137986193                       1  \n",
       "C1940951230                      0  \n",
       "C247537602                       0  \n",
       "C470079617                       1  \n",
       "C1142118359                      0  "
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Generate categorical features from the event type\n",
    "processed_events = user_events_data\n",
    "processed_events = pd.get_dummies(processed_events, columns=['event'])\n",
    "processed_events.set_index(['source'], inplace=True)\n",
    "processed_events.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extracting Labels and Training a Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The final step is to generate a target label column (the fraud yes/no indication) and train a basic model to evaluate your assumptions. The next part demonstrates how to create the labels dataset and use sklearn to train and evaluate a basic model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Label df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_labels(df):\n",
    "    labels = df[['fraud','timestamp']].copy()\n",
    "    labels = labels.rename(columns={\"fraud\": \"label\"})\n",
    "    labels['timestamp'] = labels['timestamp'].astype(\"datetime64[ms]\")\n",
    "    labels['label'] = labels['label'].astype(int)\n",
    "    return labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>label</th>\n",
       "      <th>timestamp</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>source</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>C1022153336</th>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:00:42.615</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1006176917</th>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:00.909</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1010936270</th>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:06.016</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1033736586</th>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:18.309</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C1019071188</th>\n",
       "      <td>0</td>\n",
       "      <td>2023-08-07 22:01:52.198</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             label               timestamp\n",
       "source                                    \n",
       "C1022153336      0 2023-08-07 22:00:42.615\n",
       "C1006176917      0 2023-08-07 22:01:00.909\n",
       "C1010936270      0 2023-08-07 22:01:06.016\n",
       "C1033736586      0 2023-08-07 22:01:18.309\n",
       "C1019071188      0 2023-08-07 22:01:52.198"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create the target label dataset (fraud indication)\n",
    "labels_set = create_labels(processed_transactions)\n",
    "labels_set.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fitting 3 folds for each of 100 candidates, totalling 300 fits\n",
      "Accuracy: 1.0\n",
      "Precision: 1.0\n",
      "Recall: 1.0\n",
      "F1 Score: 1.0\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {color: black;}#sk-container-id-1 pre{padding: 0;}#sk-container-id-1 div.sk-toggleable {background-color: white;}#sk-container-id-1 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-1 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-1 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-1 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-1 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-1 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-1 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-1 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-1 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-1 div.sk-item {position: relative;z-index: 1;}#sk-container-id-1 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-1 div.sk-item::before, #sk-container-id-1 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-1 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-1 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-1 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-1 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-1 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-1 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-1 div.sk-label-container {text-align: center;}#sk-container-id-1 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-1 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>RandomForestClassifier(max_depth=100, min_samples_leaf=2)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">RandomForestClassifier</label><div class=\"sk-toggleable__content\"><pre>RandomForestClassifier(max_depth=100, min_samples_leaf=2)</pre></div></div></div></div></div>"
      ],
      "text/plain": [
       "RandomForestClassifier(max_depth=100, min_samples_leaf=2)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Train a model based on the transactions, events, and labels\n",
    "from src.train_sklearn import train_and_val, prepare_data_to_train\n",
    "\n",
    "X_train, X_test, y_train, y_test = prepare_data_to_train(processed_transactions, processed_events, labels_set)\n",
    "rf_best = train_and_val(X_train, X_test, y_train, y_test)\n",
    "\n",
    "# print the model results (Accuracy, ..)\n",
    "rf_best"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "proof-reading",
   "language": "python",
   "name": "conda-env-.conda-proof-reading-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.16"
  },
  "toc-showtags": false
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
